﻿using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using HIWSystem.Common;
using HIWSystem.Helpers;
using HIWSystem.Models;
using Microsoft.Win32;
using Microsoft.WindowsAPICodePack.Dialogs;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;

namespace HIWSystem.ViewModel
{
    public class ATEReportSummaryViewModel : ViewModelBase
    {
        #region 属性

        private List<SNEntity> lstSqlSnEntity = null;
        private int errorCounts = 0;
        private bool isEnabledBatchReportButton = true;
        private bool isEnabledGenerateButton = true;
        private Visibility isVisibleLoadingAnitation = Visibility.Collapsed;
        private int preGenerateCounts = 0;
        private ObservableCollection<SNEntity> preGenerateSns;
        private string processMsg;
        private Brush processMsgForeground;
        private string saveFolderPath;

        /// <summary>
        /// 异常序列号数量
        /// </summary>
        public int ErrorCounts
        {
            get { return errorCounts; }
            set { errorCounts = value; this.RaisePropertyChanged(() => ErrorCounts); }
        }

        /// <summary>
        /// 是否启用批量导入按钮
        /// </summary>
        public bool IsEnabledBatchReportButton
        {
            get { return isEnabledBatchReportButton; }
            set { isEnabledBatchReportButton = value; this.RaisePropertyChanged(() => IsEnabledBatchReportButton); }
        }

        /// <summary>
        /// 是否启用生成报告按钮
        /// </summary>
        public bool IsEnabledGenerateButton
        {
            get { return isEnabledGenerateButton; }
            set { isEnabledGenerateButton = value; this.RaisePropertyChanged(() => IsEnabledGenerateButton); }
        }

        /// <summary>
        /// 是否显示加载动画
        /// </summary>
        public Visibility IsVisibleLoadingAnitation
        {
            get { return isVisibleLoadingAnitation; }
            set { isVisibleLoadingAnitation = value; this.RaisePropertyChanged(() => IsVisibleLoadingAnitation); }
        }

        /// <summary>
        /// 预生成报告序列号数量
        /// </summary>
        public int PreGenerateCounts
        {
            get { return preGenerateCounts; }
            set { preGenerateCounts = value; this.RaisePropertyChanged(() => PreGenerateCounts); }
        }

        /// <summary>
        /// 预生成报告序列号
        /// </summary>
        public ObservableCollection<SNEntity> PreGenerateSns
        {
            get { return preGenerateSns; }
            set { preGenerateSns = value; this.RaisePropertyChanged(() => PreGenerateSns); }
        }

        /// <summary>
        /// 过程信息文本
        /// </summary>
        public string ProcessMsg
        {
            get { return processMsg; }
            set { processMsg = value; this.RaisePropertyChanged(() => ProcessMsg); }
        }

        /// <summary>
        /// 过程信息的字体颜色
        /// </summary>
        public Brush ProcessMsgForeground
        {
            get { return processMsgForeground; }
            set { processMsgForeground = value; this.RaisePropertyChanged(() => ProcessMsgForeground); }
        }

        /// <summary>
        /// 报告保存路径
        /// </summary>
        public string SaveFolderPath
        {
            get { return saveFolderPath; }
            set { saveFolderPath = value; this.RaisePropertyChanged(() => SaveFolderPath); }
        }

        private double progressBarValue;

        /// <summary>
        /// 进度条当前值
        /// </summary>
        public double ProgressBarValue
        {
            get { return progressBarValue; }
            set { progressBarValue = value; this.RaisePropertyChanged(() => ProgressBarValue); }
        }

        private double progressBarMaximum = 100;

        /// <summary>
        /// 进度条最大值
        /// </summary>
        public double ProgressBarMaximum
        {
            get { return progressBarMaximum; }
            set { progressBarMaximum = value; this.RaisePropertyChanged(() => ProgressBarMaximum); }
        }

        private ObservableCollection<string> errorSns;

        /// <summary>
        /// 异常序列号
        /// </summary>
        public ObservableCollection<string> ErrorSns
        {
            get { return errorSns; }
            set { errorSns = value; this.RaisePropertyChanged(() => ErrorSns); }
        }

        #endregion 属性

        #region 命令

        private RelayCommand batchReportCmd;
        private RelayCommand browseCmd;

        private RelayCommand generateCmd;

        public RelayCommand BatchReportCmd
        {
            get
            {
                batchReportCmd = batchReportCmd ?? new RelayCommand(BatchReportAsync);
                return batchReportCmd;
            }
            set { batchReportCmd = value; }
        }

        public RelayCommand BrowseCmd
        {
            get
            {
                browseCmd = browseCmd ?? new RelayCommand(Browse);
                return browseCmd;
            }
            set { browseCmd = value; }
        }

        public RelayCommand GenerateCmd
        {
            get
            {
                generateCmd = generateCmd ?? new RelayCommand(GenerateAsync);
                return generateCmd;
            }
            set { generateCmd = value; }
        }

        private RelayCommand loadPageCmd;

        public RelayCommand LoadPageCmd
        {
            get
            {
                loadPageCmd = loadPageCmd ?? new RelayCommand(LoadedPage);
                return loadPageCmd;
            }
            set { loadPageCmd = value; }
        }

        #endregion 命令

        #region 方法

        /// <summary>
        /// 页面加载
        /// </summary>
        private void LoadedPage()
        {
            //SavePath = Global.AppConfigs?.ShipInfo?.FullReportDirectory; //根据配置项初始化报告保存路径
        }

        /// <summary>
        /// 添加过程信息
        /// </summary>
        /// <param name="msg">信息</param>
        /// <param name="color">字体颜色</param>
        private void AddProcessInfo(string msg, Brush color)
        {
            Application.Current.Dispatcher.BeginInvoke((Action)(() =>
            {
                ProcessMsgForeground = color;
                ProcessMsg = msg;
            }));
        }

        /// <summary>
        /// 批量导入待生成报告的序列号
        /// </summary>
        private async void BatchReportAsync()
        {
            string batchDataFilePath = string.Empty; //批量数据文件路径
            StringBuilder errorMsg = new StringBuilder();//异常信息
            bool result = false;//验证结果
            await Task.Run(() =>
            {
                try
                {
                    AddProcessInfo("******************************开始处理**************************************", Brushes.Blue);
                    IsVisibleLoadingAnitation = Visibility.Visible; //显示加载动画
                    SetButtonEnableState(false);//禁用按钮
                    AddProcessInfo("正在选择批量报告文件...", Brushes.Black);
                    OpenFileDialog openFileDialog = new OpenFileDialog();
                    openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
                    openFileDialog.Filter = "Excel文件（*.xls;*.xlsx）|*.xls;*.xlsx"; //"(*.xls, *.xlsx) | *.xls; *.xlsx ";
                    openFileDialog.Title = "选择批量报告文件";
                    if (openFileDialog.ShowDialog() == true)
                    {
                        batchDataFilePath = openFileDialog.FileName;
                        AddProcessInfo(string.Format("已选择批量报告文件，文件路径【{0}】", batchDataFilePath), Brushes.Green);
                        var excelHeaderAndEntityPropertyMapping = new Dictionary<string, string>();
                        excelHeaderAndEntityPropertyMapping.Add(nameof(SNEntity.SN), "序列号");
                        AddProcessInfo("正在验证导入的模板标题和数据上限...", Brushes.Black);
                        result = BusinessProcess.VerifyExcelTemplateHeaderAndDataUpperLimit(excelHeaderAndEntityPropertyMapping, batchDataFilePath, out errorMsg);
                        if (result)
                        {
                            AddProcessInfo("导入报告的模板标题和数据上限没有问题", Brushes.Green);
                            AddProcessInfo("正在将Excel数据添加到实体类型中...", Brushes.Black);
                            var excelEntities = BusinessProcess.ExcelToEntityList<SNEntity>(excelHeaderAndEntityPropertyMapping, batchDataFilePath, out errorMsg, 2);
                            //errorMsg为空表示表中的数据符合规范
                            if (errorMsg.ToString() == "" && excelEntities.Count > 0)
                            {
                                AddProcessInfo("验证导入的数据在系统中是否存在", Brushes.Black);
                                //将序列号基础表填充到实体集合中
                                lstSqlSnEntity = BusinessProcess.SqlTableToEntityConvert<SNEntity>("select * from vw_SN with(nolock)");
                                AddProcessInfo("正在循环检测Excel每行数据在系统中是否存在...", Brushes.Black);
                                int errorSnCount = 0;//异常的序列号数量(即系统中不存在的)
                                for (int i = 0; i < excelEntities.Count; i++)
                                {
                                    if (lstSqlSnEntity.Count(s => s.SN == excelEntities[i].SN) == 0)
                                    {
                                        errorMsg.AppendLine("Excel中第" + (i + 3) + "行的数据在系统中不存在,请检查数据填写是否有误?");
                                        errorSnCount += 1;
                                    }
                                    else
                                    {
                                        //下面判断对应机种的ATE报告配置是否存在
                                        var partnumber = lstSqlSnEntity.First(e => e.SN == excelEntities[i].SN).PN;
                                        var ATEReportConfigPath = Path.Combine(Environment.CurrentDirectory, "ATEReportSummaryConfig\\" + string.Format($"{partnumber}.xml"));
                                        if (!File.Exists(ATEReportConfigPath))
                                        {
                                            errorMsg.AppendLine("Excel中第" + (i + 3) + "行序列号【" + excelEntities[i].SN + "】对应的机种【" + partnumber + "】没有找到ATE配置文件,请先检查ATE报告的配置项是否已经配置?");
                                            errorSnCount += 1;
                                        }
                                    }
                                }
                                AddProcessInfo(string.Format("检测完毕  总共【{0}】条 满足【{1}】条 不满足【{2}】条", excelEntities.Count, excelEntities.Count - errorSnCount, errorSnCount), Brushes.Green);
                                //errorMsg为空表示没有异常数据
                                if (errorMsg.ToString() == "")
                                {
                                    AddProcessInfo("检查完成！开始将数据添加到界面列表中...", Brushes.Black);
                                    Application.Current.Dispatcher.BeginInvoke((Action)(() =>
                                    {
                                        PreGenerateSns = new ObservableCollection<SNEntity>();
                                        foreach (var e in excelEntities)
                                        {
                                            PreGenerateSns.Add(e);
                                        }
                                        PreGenerateCounts = PreGenerateSns.Count;
                                    }));
                                    AddProcessInfo("******************************处理完成**************************************", Brushes.Blue);
                                }
                                else
                                {
                                    AddProcessInfo(errorMsg.ToString(), Brushes.Red);
                                }
                            }
                            else
                            {
                                AddProcessInfo(errorMsg.ToString(), Brushes.Red);
                            }
                        }
                        else
                        {
                            AddProcessInfo(errorMsg.ToString(), Brushes.Red);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.WriteLog(string.Format("批量导入序列号数据时出现错误【{0}】", ex.Message), ex);
                    MessageBox.Show("批量导入失败！", "软件温馨提示!", MessageBoxButton.OK, MessageBoxImage.Error);
                    AddProcessInfo(string.Format("批量导入序列号数据时出现错误【{0}】", ex.Message), Brushes.Red);
                }
                finally
                {
                    Application.Current.Dispatcher.BeginInvoke((Action)(() =>
                    {
                        SetButtonEnableState(true); //启用按钮
                        IsVisibleLoadingAnitation = Visibility.Collapsed;
                    }));
                }
            });
        }

        /// <summary>
        /// 选择保存路径
        /// </summary>
        private void Browse()
        {
            //打开文件夹对话框选择保存报告的文件夹路径
            var dialog = new CommonOpenFileDialog();
            dialog.IsFolderPicker = true;
            dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            CommonFileDialogResult result = dialog.ShowDialog();
            if (result == CommonFileDialogResult.Ok)
            {
                this.SaveFolderPath = dialog.FileName;
            }
            else
            {
                this.SaveFolderPath = "";
            }
        }

        /// <summary>
        /// 生成报告
        /// </summary>
        private async void GenerateAsync()
        {
            string ATEReportPNDirectorySection = string.Empty;//存放ATE报告的机种文件夹  E:\TestData\ATE Test Data\WaveLab\PassReports\AD-0707-01
            await Task.Run(() =>
            {
                try
                {
                    //判断是否选择保存路径
                    if ((SaveFolderPath ?? "") == "")
                    {
                        MessageBox.Show("保存的目录不能为空，请选择保存的路径。", "软件温馨提示", MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                    //判断是否有待生成报告的序列号
                    if (PreGenerateCounts <= 0)
                    {
                        MessageBox.Show("没有待生成报告的序列号，请先点击批量导入按钮添加序列号。", "软件温馨提示", MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                    SetButtonEnableState(false);//禁用按钮
                    AddProcessInfo("******************************开始汇总报告**************************************", Brushes.Blue);
                    ErrorSns = new ObservableCollection<string>();
                    ErrorCounts = 0;
                    string errorMessage = string.Empty;//异常信息
                    string strSn = string.Empty;



                    //迭代每一个序列号
                    for (int i = 0; i < PreGenerateSns.Count; i++)
                    {
                        ProgressBarValue = (i + 1) * 100 / PreGenerateCounts;
                        strSn = PreGenerateSns[i].SN;
                        AddProcessInfo(string.Format("开始提取序列号【{0}】报告内容", strSn), Brushes.Black);
                        string strPN = lstSqlSnEntity.First(s => s.SN == strSn).PN;
                        //ATE报告配置项路径
                        var ATEReportConfigPath = Path.Combine(Environment.CurrentDirectory, "ATEReportSummaryConfig\\" + string.Format($"{strPN}.xml"));

                        //ATE原始Log保存根目录
                        var ATEOriginalReportRootFolder = XMLHelper.GetNoteValue<string>(ATEReportConfigPath, "ReportOriginalFolder");

                        //每个机种汇总表csv文件路径
                        var pnSummaryFilePath = Path.Combine(Path.Combine(SaveFolderPath, strPN), string.Format($"Summary_{DateTime.Now.ToString("yyyyMMdd")}.csv"));
                        //生成报告的保存路径
                        GenerateReportSavePath(pnSummaryFilePath, ATEReportConfigPath);
                        if (!Directory.Exists(ATEOriginalReportRootFolder))
                        {
                            RecordErrorSnAndCount(strSn + "--" + string.Format("ATE原始数据的根目录部分【{0}】不存在,请检查配置中关于报告的路径信息!",ATEOriginalReportRootFolder));
                            continue;
                        }
                        var snReportPathInfos = new List<string>();

                        //分别提取PASS和Fail的报告，因为PASS和Fail的放在两个地址下。
                        //提取PASS的记录
                        //C:\Users\15051\Desktop\TestData\ATE Test Data\PassReports\ANT-DIP-0708-M-4E1
                        var reportPathInfo = FetchReportPathInfoBySn(strSn, strPN, "PassReports", ATEOriginalReportRootFolder);
                        if (reportPathInfo != null)
                            snReportPathInfos.Add(reportPathInfo);

                        //提取FAIL的记录
                        //C:\Users\15051\Desktop\TestData\ATE Test Data\FailReports\ANT-DIP-0708-M-4E1
                        reportPathInfo = FetchReportPathInfoBySn(strSn, strPN, "FailReports", ATEOriginalReportRootFolder);
                        if (reportPathInfo != null)
                            snReportPathInfos.Add(reportPathInfo);

                        if (snReportPathInfos.Count==0)
                        {
                            RecordErrorSnAndCount(strSn + "--" + string.Format("没有找到序列号【{0}】的测试记录!",strSn));
                            continue;
                        }
                        string strFullReportPNDirec = Path.Combine(SaveFolderPath, strPN);
                        //如果路径不存在就创建
                        if (!Directory.Exists(strFullReportPNDirec))
                            Directory.CreateDirectory(strFullReportPNDirec);
                        //循环遍历每个报告的路径信息
                        foreach (var snReportPathInfo in snReportPathInfos)
                        {
                            //最后一次测试报告文件名称
                            string strSnLatestTestReportFileName = Path.GetFileName(snReportPathInfo);
                            //完整报告路径
                            string strBackupReportPath = Path.Combine(strFullReportPNDirec, strSnLatestTestReportFileName);// Path.Combine(strFullReportPNDirec, strSnLatestTestReportFileName);  2023-3-8 修改一版 最终文件名以输入的序列号为准
                            var fullReportPathFileInfo = new FileInfo(strBackupReportPath);
                            //目标路径不存在就创建
                            if (!fullReportPathFileInfo.Exists)
                                fullReportPathFileInfo.Create().Close();
                            //先关闭Excel
                            BusinessProcess.KillProcess("EXCEL");
                            File.Copy(snReportPathInfo, strBackupReportPath, true);
                            //文件已经拷贝成功
                            AddProcessInfo(string.Format("文件已经成功拷贝到路径【{0}】下", strFullReportPNDirec), Brushes.Black);

                            //创建操作EXCEL相关对象
                            Microsoft.Office.Interop.Excel.Application excelApp = null;
                            Microsoft.Office.Interop.Excel.Workbook excelWB = null;
                            Microsoft.Office.Interop.Excel.Worksheet excelWS = null;
                            try
                            {
                                excelApp = new Microsoft.Office.Interop.Excel.Application();
                                excelWB = excelApp.Workbooks.Open(strBackupReportPath);   //创建工作簿（WorkBook：即Excel文件主体本身）
                                excelWS = (Microsoft.Office.Interop.Excel.Worksheet)excelWB.Worksheets[1];   //创建工作表（即Excel里的子表sheet） 1表示在子表sheet1里进行数据导出

                                var statisticsItemElements = XMLHelper.GetNodeElements(ATEReportConfigPath, "Item");
                                var arrStatisticsValues = new List<string>();
                                arrStatisticsValues.Add(strSn);
                                foreach (var itemElement in statisticsItemElements)
                                {
                                    var cell = itemElement.Attribute("Cell").Value;
                                    var itemValue = excelWS.Range[cell].Value2.ToString();
                                    arrStatisticsValues.Add(itemValue);
                                }
                                BusinessProcess.AddContentToCsv<string[]>(pnSummaryFilePath, arrStatisticsValues.Select(value => $"\"{value}\"").ToArray());
                                AddProcessInfo(string.Format("文件【{0}】内容提取完成", strSnLatestTestReportFileName), Brushes.Black);
                            }
                            catch (Exception ex)
                            {
                                LogHelper.WriteLog(string.Format("打开Excel提取数据时出现异常【{0}】", ex.Message));
                                Application.Current.Dispatcher.BeginInvoke((Action)(() =>
                                {
                                    ErrorSns.Add(strSn + "--" + string.Format("打开Excel数据时出现异常【{0}】", ex.Message));
                                    ErrorCounts += 1;
                                }));
                            }
                            finally
                            {
                                //释放资源
                                excelWB?.Close();
                                excelApp?.Quit();
                                File.Delete(strBackupReportPath);//删除复制过来的报告源文件
                            }
                        }
                       
                       
                    }
                    AddProcessInfo("******************************汇总报告完成**************************************", Brushes.Blue);
                }
                catch (Exception ex)
                {
                    LogHelper.WriteLog(ex.Message, ex);
                    MessageBox.Show("生成ATE汇总报告失败！", "软件温馨提示!", MessageBoxButton.OK, MessageBoxImage.Error);
                    AddProcessInfo(string.Format("生成ATE汇总报告数据时出现错误【{0}】", ex.Message), Brushes.Red);
                }
                finally
                {
                    Application.Current.Dispatcher.BeginInvoke((Action)(() =>
                    {
                        SetButtonEnableState(true);//启用按钮
                    }));
                }
            });
        }
        /// <summary>
        /// 提取报告的路径信息
        /// </summary>
        /// <param name="sn">序列号</param>
        /// <param name="pn">机种</param>
        /// <param name="resultFolderName">结果目录部分名称<PassedReports|FailedReports></param>
        /// <param name="originalReportRootFolder">原始报告的根目录</param>
        /// <returns></returns>
        private string FetchReportPathInfoBySn(string sn,string pn,string resultFolderName,string originalReportRootFolder)
        {
            try
            {
                string searchPattern = string.Empty;
                searchPattern = "^" + (pn ?? "") + "_" + //匹配机种
                (sn ?? "") + "_" + //匹配序列号
                "SParTest_(Passed|Failed)_" + //匹配测试结果部分
                "((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})(((0[13578]|1[02])(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)(0[1-9]|[12][0-9]|30))|(02(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))0229))" + //匹配年月日部分 20220827
                "T" + //匹配字符T
                "([0-1][0-9]|2[0-3])([0-5][0-9])([0-5][0-9])" + //匹配时间部分092352
                "\\.xls$"; //匹配文件格式
                           //分别提取PASS和Fail的报告，因为PASS和Fail的放在两个地址下。
                           //提取PASS的记录
                var regex = new Regex(searchPattern);
                //C:\Users\15051\Desktop\TestData\ATE Test Data\PassReports\ANT-DIP-0708-M-4E1
                var ATEOriginalReportPnFolderSection = Path.Combine(originalReportRootFolder, string.Format($"{resultFolderName}\\{pn}")); //"PassReports\\" + pn);

                var diATEOriginalReportPnFolder = new DirectoryInfo(ATEOriginalReportPnFolderSection);
                //提取序列号命名的文件夹
                var diATEOriginalReportSnFolder = diATEOriginalReportPnFolder.GetDirectories(string.Format($"{sn}*"));
                //提取最后一次测试记录
                var specificResultReportFiles = diATEOriginalReportSnFolder.Select(d => d.GetFiles("*.xls", SearchOption.AllDirectories))
                                                                          .SelectMany(f => f)
                                                                          .Where(p => regex.IsMatch(p.Name))
                                                                          .OrderByDescending(p => p.Name.Split('_')[4].Replace("T", ""))
                                                                          .FirstOrDefault()?.FullName; ;
                return specificResultReportFiles;
            }
            catch (Exception ex)
            {
                AddProcessInfo("提取报告的路径信息时出现错误",Brushes.Black);
                throw new Exception("提取报告的路径信息时出现错误:"+ex.Message);
            }
           
        }
        /// <summary>
        /// 生成ATE汇总报告的保存路径
        /// </summary>
        /// <param name="sn">序列号</param>
        /// <param name="pn">机种</param>
        private void GenerateReportSavePath(string summaryPath, string configPath)
        {
            //汇总报告路径部分
            var summaryFolder = Path.GetDirectoryName(summaryPath);
            if (!Directory.Exists(summaryFolder))
            {
                Directory.CreateDirectory(summaryFolder);
            }
            var summaryReportSavePath = new FileInfo(summaryPath);
            if (!summaryReportSavePath.Exists)
            {
                //创建csv文件
                summaryReportSavePath.Create().Close();
                //获取所有统计项
                var lstStatisticsItems = XMLHelper.GetNoteAttributeValue<string>(configPath, "Item", "Name").Select(value => $"\"{value}\"").ToList(); //Select(value => $"\"{value}\"")这个是防止Item里面有逗号存在
                //csv标题
                var header = string.Format("SN,{0}", string.Join(",", lstStatisticsItems));
                //添加标题到csv文件中
                BusinessProcess.AddContentToCsv<string>(summaryPath, header);
            }
        }

        /// <summary>
        /// 记录异常序列号信息和数量
        /// </summary>
        private void RecordErrorSnAndCount(string errMsg)
        {
            Application.Current.Dispatcher.BeginInvoke((Action)(() =>
            {
                ErrorSns.Add(errMsg);
                ErrorCounts += 1;
            }));
        }

        /// <summary>
        /// 设置按钮的启用状态
        /// </summary>
        /// <param name="state">状态(true/false)</param>
        private void SetButtonEnableState(bool state)
        {
            Application.Current.Dispatcher.BeginInvoke((Action)(() =>
            {
                IsEnabledBatchReportButton = state;
                IsEnabledGenerateButton = state;
            }));
        }

        #endregion 方法
    }
}